/*******************************************************************************
 * Copyright (c) 2003 Boris Pruessmann and others. All rights reserved. This
 * program and the accompanying materials are made available under the terms of
 * the Common Public License v0.5 which accompanies this distribution, and is
 * available at http://www.eclipse.org/legal/cpl-v05.html
 *
 * Contributors: Boris Pruessmann - Initial implementation.
 ******************************************************************************/
package net.sourceforge.perforce.core.internal;

import com.google.common.base.Function;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;

import net.sourceforge.perforce.core.PerforceCorePlugin;
import net.sourceforge.perforce.core.PerforceException;
import net.sourceforge.perforce.core.api.IPerforceServer;
import net.sourceforge.perforce.core.api.IWhereResult;

import org.eclipse.core.internal.resources.ProjectDescription;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IProjectDescription;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspace;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.core.runtime.jobs.MultiRule;
import org.eclipse.team.core.ProjectSetCapability;
import org.eclipse.team.core.ProjectSetSerializationContext;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.team.core.TeamException;

import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

/**
 * @version $Revision: 496 $
 * @author <a href="mailto:bpruessmann@users.sourceforge.net">Boris
 *         Pruessmann</a>
 */
public class P4ProjectSetCapability extends ProjectSetCapability {
  //----------------------------------------------------------------------------
  // -- Public Methods

  @Override
  public String[] asReference(IProject[] providerProjects, ProjectSetSerializationContext context,
      IProgressMonitor monitor) throws TeamException {
    String[] result = new String[providerProjects.length];
    for (int i = 0; i < providerProjects.length; i++) {
      StringBuilder buffer = new StringBuilder();
      buffer.append("1.0,"); //$NON-NLS-1$

      IProject project = providerProjects[i];
      P4TeamProvider provider = (P4TeamProvider) RepositoryProvider.getProvider(project);

      IPerforceServer server = provider.getPerforceServer();
      buffer.append(server.getPort());
      buffer.append(',');

      IWhereResult whereResult = server.where(project);
      buffer.append(whereResult.getDepotFilename());
      buffer.append(',');

      buffer.append(project.getName());

      result[i] = buffer.toString();
    }

    return result;
  }

  @Override
  public IProject[] addToWorkspace(String[] referenceStrings,
      ProjectSetSerializationContext context, IProgressMonitor monitor) throws TeamException {
    final int size = referenceStrings.length;

    IPerforceServer[] servers = PerforceCorePlugin.getServerManager().getPerforceServers();
    Map<String, IPerforceServer> serverMap = Maps.newHashMap();
    for (int i = 0; i < servers.length; i++) {
      IPerforceServer server = servers[i];
      if (!serverMap.containsKey(server.getPort())) {
        serverMap.put(server.getPort(), server);
      }
    }

    final IProject[] projects = new IProject[size];
    final IPerforceServer[] projectServers = new IPerforceServer[size];
    final String[] projectLocation = new String[size];

    for (int i = 0; i < size; i++) {
      StringTokenizer tokenizer = new StringTokenizer(referenceStrings[i], ","); //$NON-NLS-1$
      String version = tokenizer.nextToken();
      if (!version.equals("1.0")) //$NON-NLS-1$
      {
        return null;
      }

      String repo = tokenizer.nextToken();
      projectServers[i] = serverMap.get(repo);
      projectLocation[i] = tokenizer.nextToken();

      String projectName = tokenizer.nextToken();
      projects[i] = ResourcesPlugin.getWorkspace().getRoot().getProject(projectName);
    }

    final int[] num = new int[] {size};
    for (int i = 0; i < size; i++) {
      IProject project = projects[i];
      if (projectServers[i] == null || project.exists()) {
        projectLocation[i] = null;
        num[0]--;
      }
    }

    IWorkspaceRunnable op = new IWorkspaceRunnable() {
      public void run(IProgressMonitor monitor) throws CoreException {
        monitor.beginTask("", 1000 * num[0]); //$NON-NLS-1$
        try {
          for (int i = 0; i < size; i++) {
            if (projectLocation[i] != null) {
              checkout(projectServers[i], projectLocation[i], projects[i], Policy.subMonitorFor(
                  monitor, 1000));
            }
          }
        } finally {
          monitor.done();
        }
      }
    };

    try {
      ResourcesPlugin.getWorkspace().run(op, getRule(projects), IWorkspace.AVOID_UPDATE, monitor);
    } catch (CoreException e) {
      throw new TeamException(e.getStatus());
    }

    List<IProject> result = Lists.newArrayList();
    for (int i = 0; i < projects.length; i++) {
      if (projectLocation[i] != null) {
        result.add(projects[i]);
      }
    }

    return result.toArray(new IProject[result.size()]);
  }

  private ISchedulingRule getRule(IProject[] projects) {
    List<ISchedulingRule> rules = Lists.newArrayList(
        Iterables.transform(Arrays.asList(projects), new Function<IProject, ISchedulingRule>() {
      public ISchedulingRule apply(IProject from) {
        return ResourcesPlugin.getWorkspace().getRuleFactory().modifyRule(from);
      }
    }));
    return MultiRule.combine(rules.toArray(new ISchedulingRule[rules.size()]));
  }

  //----------------------------------------------------------------------------
  // - Private Methods

  private void checkout(IPerforceServer server, String projectLocation, IProject project,
      IProgressMonitor monitor) throws PerforceException {
    IWhereResult whereResult = server.where(projectLocation);
    IPath location = new Path(whereResult.getLocalFilename());
    monitor.worked(200);

    IProjectDescription projectDescription = new ProjectDescription();
    projectDescription.setName(project.getName());
    projectDescription.setLocation(location);
    monitor.worked(100);

    try {
      project.create(projectDescription, Policy.subMonitorFor(monitor, 100));

      if (!project.isOpen()) {
        project.open(Policy.subMonitorFor(monitor, 100));
      } else {
        project.refreshLocal(IResource.DEPTH_INFINITE, Policy.subMonitorFor(monitor, 100));
      }
    } catch (CoreException e) {
      throw new PerforceException(e.getStatus());
    }

    PerforceCorePlugin.getServerManager().setSharing(project, server,
        Policy.subMonitorFor(monitor, 100));

    P4TeamProvider provider = (P4TeamProvider) RepositoryProvider.getProvider(project);
    provider.refreshState(new IResource[] {project}, IResource.DEPTH_INFINITE, Policy
        .subMonitorFor(monitor, 400));
  }
}
